package com.millstein.mq;

import com.millstein.model.ProductMessage;
import com.millstein.service.ProductTaskService;
import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * @description:
 * @author: tsing
 * @create: 2021-09-21 15:02
 */
@Slf4j
@Component
@RabbitListener(queues = "${mqconfig.product-release-queue}")
public class ProductReleaseListener {

    @Autowired
    private ProductTaskService productTaskService;

    @RabbitHandler
    public void releaseProduct(ProductMessage productMessage, Message message, Channel channel) {
        log.info("收到了库存解锁的消息，内容是：{}", productMessage);

        // 1.获取消息投递的标识，用来进行手动确认
        long deliveryTag = message.getMessageProperties().getDeliveryTag();

        try {
            // 2.解锁库存，返回是否需要重新投递的标识
            boolean retry = productTaskService.doReleaseProduct(productMessage);

            // 3.根据是否需要重新投递分别执行（retry为true时重新投递，false时直接确认）
            doAfterReleaseByRetry(channel, deliveryTag, retry, productMessage);
        } catch (Exception e) {

            // 4.如果发生异常，那么也要进行后续处理（重新投递）
            doAfterReleaseWhileOccurredException(channel, deliveryTag, e, productMessage);
        }
    }

    /**
     * 根据是否需要重新投递分别执行。当retry为true时进行重新投递，为false时手动确认消息
     * @param channel 消息的通道
     * @param deliveryTag 消息投递的标识
     * @param retry 是否需要重新投递
     * @param productMessage 消息的内容
     */
    private void doAfterReleaseByRetry(Channel channel, long deliveryTag, boolean retry, ProductMessage productMessage) {
        try {
            if (retry) {
                log.error("库存解锁失败，需要重新投递：{}", productMessage);
                channel.basicReject(deliveryTag, true);
            } else {
                log.info("库存解锁成功：{}", productMessage);
                channel.basicAck(deliveryTag, false);
            }
        } catch (Exception e) {
            // TODO 这里可以直接写入数据库，等待人工核查
            log.error("后续处理失败：{}，原因是：{}", productMessage, e.getMessage(), e);
        }
    }

    /**
     * 在释放库存的过程中如果发生异常，那么也要重新投递
     * @param channel 消息的通道
     * @param deliveryTag 消息投递的标识
     * @param e 异常对象
     * @param productMessage 消息的内容
     */
    private void doAfterReleaseWhileOccurredException(Channel channel, long deliveryTag, Exception e, ProductMessage productMessage) {
        try {
            log.error("解锁库存时发生了异常：{}，消息内容是：{}", e.getMessage(), productMessage, e);
            channel.basicReject(deliveryTag, true);
        } catch (Exception ex) {
            // TODO 这里可以直接写入数据库，等待人工核查
            log.error("异常处理失败：{}，原因是：{}", productMessage, e.getMessage(), e);
        }
    }

    @RabbitHandler
    public void handleNormalMessage(String normalMessage, Message message, Channel channel) {
        log.info("收到了普通的消息，内容是：{}", normalMessage);
    }
}
